#include "../lib_include/FileUtil.h"
#include "../lib_include/Logger.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

using namespace netlib;

FileUtil::AppendFile::AppendFile(StringArg filename)
  : fp_(::fopen(filename.c_str(), "ae")), // 打开文件获得fd
    writtenBytes_(0)
{
  assert(fp_);
  // setbuffer(FILE * stream,char * buf,size_t size)
  ::setbuffer(fp_, buffer_, sizeof buffer_);
}

FileUtil::AppendFile::~AppendFile() 
{
  ::fclose(fp_); // 关闭fd
}

// 向日志文件中写入数据logline
void FileUtil::AppendFile::append(const char* logline, const size_t len)
{
  size_t written = 0;

  while (written != len)
  {
    size_t remain = len - written;
    size_t n = write(logline + written, remain); // 非系统调用，手动封装的函数
    if (n != remain)
    {
      int err = ferror(fp_);
      if (err)
      {
        fprintf(stderr, "AppendFile::append() failed %s\n", strerror_tl(err));
        break;
      }
    }
    written += n;
  }

  writtenBytes_ += written; // 空闲指针向后偏移
}

void FileUtil::AppendFile::flush() // 冲洗文件流缓冲区
{
    ::fflush(fp_);
}

// 实际的向文件写入的函数
size_t FileUtil::AppendFile::write(const char* logline, size_t len)
{
    // #undef fwrite_unlocked
    return ::fwrite_unlocked(logline, 1, len, fp_);
}

FileUtil::ReadSmallFile::ReadSmallFile(StringArg filename) // 打开文件获得fd
  : fd_(::open(filename.c_str(), O_RDONLY | O_CLOEXEC)),
    err_(0)
{
    buf_[0] = '\0';
    if (fd_ < 0)
        err_ = errno;
}

FileUtil::ReadSmallFile::~ReadSmallFile()
{
    if (fd_ >= 0)
        ::close(fd_); // 关闭fd
}


template<typename String>
int FileUtil::ReadSmallFile::readToString( // 读取文件数据到string对象中
    int maxSize,            // 文件最大尺寸
    String* content,        // 传入一个string对象指针
    int64_t* fileSize,      // 文件大小
    int64_t* modifyTime,    // 文件的权限、属主、组或内容上一次被修改的时间
    int64_t* createTime)    // 文件的内容上一次被修改的时间
{
    static_assert(sizeof(off_t) == 8, "_FILE_OFFSET_BITS = 64");
    assert(content != NULL);
    int err = err_;
    if (fd_ >= 0) // 判断fd是否有效
    {
        content->clear(); // 清空string

        if (fileSize)
        {
            struct stat statbuf;
            if (::fstat(fd_, &statbuf) == 0) // 将fd的文件属性赋值到statbuf中
            {
                if (S_ISREG(statbuf.st_mode)) // 判断是否为普通文件
                {
                    *fileSize = statbuf.st_size; // 获取文件大小

                    //防止超过上限
                    content->reserve(static_cast<int>(std::min(implicit_cast<int64_t>(maxSize), *fileSize)));
                }
                else if (S_ISDIR(statbuf.st_mode))
                    err = EISDIR;
                if (modifyTime)
                    *modifyTime = statbuf.st_mtime; // 获得最后一次修改的时间
                if (createTime)
                    *createTime = statbuf.st_ctime; // 获得最后一次修改的时间
            }
            else
                err = errno; // 设置错误码
        }

        // 将文件数据读取到缓冲区
        while (content->size() < implicit_cast<size_t>(maxSize))
        {
            size_t toRead = std::min(implicit_cast<size_t>(maxSize) - content->size(), sizeof(buf_));
            ssize_t n = ::read(fd_, buf_, toRead);
            if (n > 0)
                content->append(buf_, n);
            else
            {
                if (n < 0)
                    err = errno;
                break;
            }
        }
    }
    return err;
}

// ssize_t pread(int fd, void *buf, size_t count, off_t offset)--原子操作
// 读取文件fd中的数据复制到buf中，从offset位置开始，读取count字节
int FileUtil::ReadSmallFile::readToBuffer(int* size) // 将数据从文件读到缓冲区
{
  int err = err_;
  if (fd_ >= 0)
  {
    ssize_t n = ::pread(fd_, buf_, sizeof(buf_)-1, 0);
    if (n >= 0)
    {
      if (size)
        *size = static_cast<int>(n); // 记录实际读取到的数据量
      buf_[n] = '\0'; // 缓冲区尾端结尾
    }
    else
    {
      err = errno; // 未读到数据则设置错误码
    }
  }
  return err;
}

template int FileUtil::readFile(
    StringArg filename,
    int maxSize,
    string* content,
    int64_t*, int64_t*, int64_t*);

template int FileUtil::ReadSmallFile::readToString(
    int maxSize,
    string* content,
    int64_t*, int64_t*, int64_t*);

